在昨天的開發中,如果我們依賴手動輸入一個個數字來定義地圖,一旦地圖範圍稍微擴大,輸入數千個數據就會耗費大量時間與人力。更重要的是,在 3D 空間中,過小的手刻地圖(例如 10x10 或 12x12)會讓鏡頭邊緣輕易穿幫,露出外部的空無背景,無法呈現出具備探索感的廣闊地城。
為了解決這個問題,今天我們將利用「程序化生成(Procedural Generation)」的技巧,透過雙層迴圈與隨機機率,瞬間建構出一個 50x50 的二維地圖資料陣列,並在其中融合隨機地貌與明確的關卡主幹道
【Step 1:用迴圈與機率,自動鋪滿多樣化草地】
要建立一個 50x50 的地圖,我們需要定義網格的長寬維度,並準備好存放地圖資訊的資料結構。
請打開 src/main.js,將昨天手寫的 mapGrid 陣列刪除並在 src/main.js 中宣告常數 gridRows 與 gridCols 為 50,並建立空陣列 mapGrid 作為地圖的主容器。除了記錄地板種類外,立體地城後續還需要記錄每一個座標上的「垂直高度」,因此我們同步建立了一個與地圖等大的二維陣列 wallHeightMap,利用 Array.from 將所有初始數值填滿為 0
// ==========================================
// 3. 地圖陣列與殘破城牆建築生成
// ==========================================
const gridRows = 50;
const gridCols = 50;
const mapGrid = [];
const wallHeightMap = Array.from({ length: gridRows }, () => new Array(gridCols).fill(0));
【Step 2:雙層迴圈與機率分佈——鋪設基礎石磚地貌】
有了資料結構後,我們不能讓整片地板鋪滿單調一致的相同磚紋,否則視覺會非常僵硬死板。我們利用外層 row 迴圈與內層 col 迴圈依序巡訪每一個格子,並引入 Math.random() 進行隨機採樣。
// 1) 鋪滿基礎地面 (隨機變體)
for (let row = 0; row < gridRows; row++) {
const rowArray = [];
for (let col = 0; col < gridCols; col++) {
let tile = 0;
const rand = Math.random();
if (rand < 0.15) tile = 3;
else if (rand < 0.20) tile = 4;
rowArray.push(tile);
}
mapGrid.push(rowArray);
}
【Step 3:開闢中央主路線與動態碎石障礙】
純隨機的地圖雖然具備自然感,但缺乏遊戲關卡所必需的「動線引導」。如果沒有主要道路,玩家在偌大的空間中會失去探索目標。因此,我們在生成完隨機底層後,透過固定範圍的迴圈強制覆寫特定網格[cite: 9]。
我們讓迴圈索引 i 從 8 運行到 41,在地圖正中央強行鋪設一條筆直延伸的主走道(寬度為 2 格),並在走道左右兩側加入機率性凸起的碎石塊[cite: 9]。
// 2) 中央主路線與路邊碎石
for (let i = 8; i < 42; i++) {
mapGrid[i][24] = 1; //[cite: 9]
mapGrid[i][25] = 1; //[cite: 9]
if (Math.random() < 0.22) mapGrid[i][23] = 2; //[cite: 9]
if (Math.random() < 0.22) mapGrid[i][26] = 2; //[cite: 9]
}
【本日結語】
至此,我們已經在記憶體中建立了一個 50x50、層次分明的地圖陣列 mapGrid[cite: 9]。它具備大面積的基礎石磚、隨機點綴的質感變體、筆直貫穿地圖的中央走廊,以及路旁零散散落的碎石障礙[cite: 9]。
明天,我們將進入 HD-2D 的靈魂核心——鏡頭設定,解決 3D 廣角鏡頭邊緣拉扯變形的問題,打造專屬的 2.5D 固定平俯視角!